﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.IO;
using System.Xml;

namespace XmlDSigEx
{
    public class RSAX509Algorithm : RsaAlgorithm
    {
        private X509Certificate2 _certificate;

        public X509Certificate2 Certificate
        {
            get { return _certificate; }
            set { _certificate = value; }
        }
        private X509Data _keyInfo;

        public RSAX509Algorithm(X509Certificate2 certificate)
            : base(certificate.PublicKey.Key as RSACryptoServiceProvider,
                certificate.PrivateKey as RSACryptoServiceProvider)
        {
            _certificate = certificate;
        }


        internal RSAX509Algorithm(XmlElement signatureElement)
            : base(signatureElement)
        {
            XmlElement keyInfoElement = signatureElement.SelectSingleNode("child::*[local-name() = 'KeyInfo']") as XmlElement;
            foreach (XmlElement keyInfoChild in keyInfoElement.ChildNodes)
            {
                if (keyInfoChild.LocalName == "X509Data")
                {
                    ProcessX509Data(keyInfoChild);
                    return;
                }
            }
        }


        private void ProcessX509Data(XmlElement keyInfoChild)
        {
            XmlElement x509Element = keyInfoChild.FirstChild as XmlElement;
            if (x509Element.LocalName != "X509Certificate")
                throw new InvalidOperationException("Can only process X509Certificate data.");

            byte[] rawCert = Convert.FromBase64String(x509Element.InnerText);
            _certificate = new X509Certificate2(rawCert);
            base.PublicKey = _certificate.PublicKey.Key as RSACryptoServiceProvider;
        }

        public override void WriteKeyInfo(XmlWriter writer)
        {
            if ((KeyInfo & X509Data.X509Certificate) == X509Data.X509Certificate)
            {
                if (_certificate == null)
                    throw new InvalidOperationException("X509 certificate data could not be written as there was no certificate provided.");
                X509DataWriter x509Writer = new X509DataWriter(_certificate);
                x509Writer.WriteXml(writer);
            }
            base.WriteKeyInfo(writer);
        }

        public override bool WillWriteKeyInfo
        {
            get
            {
                return KeyInfo != X509Data.None || base.WillWriteKeyInfo;
            }
        }

        public X509Data KeyInfo
        {
            get
            {
                return _keyInfo;
            }
            set
            {
                _keyInfo = value;
            }
        }

    }

    [Flags]
    public enum X509Data
    {
        None = 0,
        X509Certificate
    }
}
